//////////////////////////////////////////////////////////////////////////
#include <Application/GameWindow.hpp>
#include <Application/WindowManager.hpp>
#include <Rocket/Debugger/Debugger.h>
#include <thread>


//////////////////////////////////////////////////////////////////////////
// Just an experiment.
static bool			sOverUI			=	false;
static std::string	sElementId		=	"";


//////////////////////////////////////////////////////////////////////////
Application::GameWindow::Creator_t	Application::GameWindow::sCreator	=	&Application::GameWindow::Create;


void Application::GameWindow::SetCreator( Application::GameWindow::Creator_t c )
{
	assert( c );
	sCreator	=	c;
}


Application::GameWindow* Application::GameWindow::Create(
	WindowManager*				winMgr,
	sf::VideoMode				mode,
	const sf::String&			title,
	sf::Uint32					style,
	const sf::ContextSettings&	settings
	)
{
	return new GameWindow( winMgr, mode, title, style, settings );
}


//////////////////////////////////////////////////////////////////////////
Application::GameWindow::GameWindow(
	WindowManager*				winMgr,
	sf::VideoMode				mode,
	const sf::String&			title,
	sf::Uint32					style,
	const sf::ContextSettings&	settings
)
:	sf::RenderWindow() //( mode, title, style, settings )
,	mpWindowManager( winMgr )
,	mName( title )
,	mToggleFullscreen( 0 )
,	mFullscreen( false )
,	mHasFocus( true )
,	CONFIG_INIT( mPosition, std::string( "Window/" )+title+"/Position", Math::Point2i::Zero() )
,	CONFIG_INIT( mSize, std::string( "Window/" )+title+"/Size", Math::Point2i( 800, 600 ) )
#ifdef OS_WINDOWS
,	CONFIG_INIT( mMaximized, std::string( "Window/" )+title+"/Maximized", false )
,	CONFIG_INIT( mMaximizedPos, std::string( "Window/" )+title+"/MaximizedPos", Math::Point2i::Zero() )
#endif
,	mpRenderer( new RocketRenderInterface() )
,	mClock( winMgr->Clock() )
{
	// Setup the window.
	mode.width	=	mSize.X();
	mode.height	=	mSize.Y();
	create( mode, title, style, settings );

	mContext.Initialize( title.toAnsiString(), Math::Point2i( mode.width, mode.height ), mpRenderer );
	mContext.InitDebugger();
}


Application::GameWindow::~GameWindow()
{
	mpRenderer->RemoveReference();
}


bool Application::GameWindow::Update()
{
	mClock.Update();

	_UpdateSizePosition();

	sf::Event	event;
	while( pollEvent( event ) )
	{
		// Check if ant wants the event.
		DEVBUILD( if( !TwEventSFML( &event, 2, 0 ) ) )
		{
			if( !mContext.ProcessInput( event ) )
			{
				switch( event.type )
				{
				case sf::Event::Closed:
					LOG( INFO ) << "Closing window.";
					close();
					return false;
				case sf::Event::Resized:
					break;
				case sf::Event::LostFocus:
					{
						LOG( INFO ) << "Window( " << mName << " ) lost focus.";
						mHasFocus	=	false;
					}
					break;
				case sf::Event::GainedFocus:
					{
						LOG( INFO ) << "Window( " << mName << " ) gained focus.";
						mHasFocus	=	true;
					}
					break;
				case sf::Event::MouseEntered:
					{
						LOG( INFO ) << "Window( " << mName << " ) mouse entered.";
					}
					break;
				case sf::Event::MouseLeft:
					{
						LOG( INFO ) << "Window( " << mName << " ) mouse left.";
					}
					break;
				case sf::Event::JoystickConnected:
				case sf::Event::JoystickDisconnected:
				case sf::Event::JoystickMoved:
				case sf::Event::JoystickButtonPressed:
				case sf::Event::JoystickButtonReleased:
				case sf::Event::MouseMoved:
				case sf::Event::MouseButtonPressed:
				case sf::Event::MouseButtonReleased:
				case sf::Event::MouseWheelMoved:
				case sf::Event::TextEntered:
				case sf::Event::KeyPressed:
				case sf::Event::KeyReleased:
					break;
				}
			}

#ifdef ENABLE_DEVBUILD
			if( event.type==sf::Event::KeyPressed && event.key.code == sf::Keyboard::F5 )
			{
				// Eat the event.
				mpWindowManager->ToggleDebugPanel();
				continue;
			}
			
			if( event.type==sf::Event::KeyPressed && event.key.code == sf::Keyboard::F6 )
			{
				mContext.Reload( mpWindowManager->Script() );
				continue;
			}
#endif
		}
	}


	DEVBUILD(
		Rocket::Debugger::SetVisible( mpWindowManager->RocketDebuggerShow() );
	)

	// Update and render the context.
	mContext.Update();
	mContext.Render();

	//////////////////////////////////////////////////////////////////////////
	// Various testing items.  These items will eventually get wrapped and
	// cleaned in nice little systems.  Right now they are just dealt with
	// here.

	// Given a little annoyance in AntTweakBar, we delay transition to
	// fullscreen by 100ms such that mouse up is processed after clicking
	// the bool but before switching resolution.
	if( mToggleFullscreen>0 )
	{
		std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
		--mToggleFullscreen;
		if( mToggleFullscreen==0 )
			LOG( INFO ) << "Should have flipped fullscreen mode.";
	}


#ifdef ENABLE_DEVBUILD
	bool	overUI	=	mContext.OverUI();
	sOverUI			=	overUI;
	Rocket::Core::Element*	elem	=	mContext.GetHoverElement();
	sElementId		=	(overUI && elem!=nullptr) ? elem->GetId().CString() : "";
#endif

	return isOpen();
}


//////////////////////////////////////////////////////////////////////////
// Interface thoughts.
/*
	Example:
	WindowManager::LoadFont( "" );
	UILayerRef		uiRef		=	AppWindow->CreateUILayer( uint32_t layer, "name" );
	UIDocumentRef	uiDoc		=	uiRef->LoadDocument( "somedoc.rml" );

	UIPropertyRef	uiBinding	=	uiDoc->BindProperty( "top", someUint32_1 );

	// Auto update the element every 100 ms.  Without a time it updates every frame which is unneeded waste.
	// We don't keep a ref, don't care about it.
	uiDoc->BindElement( "score-value", someUint32_2, std::chrono::milliseconds( 100 ) );
	uiDoc->Show();
*/
//////////////////////////////////////////////////////////////////////////


Rocket::Core::ElementDocument* Application::GameWindow::LoadDocument( const std::string& name )
{
	setActive();

	Rocket::Core::ElementDocument*	doc	=	mContext.LoadDocument( name );
	return doc;
}


void Application::GameWindow::SetFullscreen( bool fs )
{
	if( mFullscreen!=fs )
	{
		mToggleFullscreen	=	15;
		mFullscreen			=	fs;
	}
}


void Application::GameWindow::close()
{
	mContext.UnloadAllDocuments();
	sf::RenderWindow::close();
}


void Application::GameWindow::onCreate()
{
	mpRenderer->SetWindow( this );

	if( mFullscreen )
	{
	}
	else
	{
#ifdef OS_WINDOWS
		if( mMaximized )
		{
			setPosition( sf::Vector2i( mMaximizedPos.X(), mMaximizedPos.Y() ) );

			sf::WindowHandle	handle			=	getSystemHandle();
			ShowWindow( handle, SW_SHOWMAXIMIZED );
			LOG( INFO ) << "Maximizing window.";
		}
		else
#endif
		{
			setPosition( sf::Vector2i( mPosition.X(), mPosition.Y() ) );
			setSize( sf::Vector2u( mSize.X(), mSize.Y() ) );
		}
	}
}


void Application::GameWindow::onResize()
{
	mpRenderer->Resize();

	sf::Vector2u	size	=	getSize();
	mContext.SetDimensions( size.x, size.y );

#ifdef OS_WINDOWS
	if( !Maximized() )
#endif
	{
		mSize.Set( size.x, size.y );
	}

	LOG( INFO ) << "Window( " << mName << " ) resized to: " << size.x << ", " << size.y;
}


void Application::GameWindow::_UpdateSizePosition()
{
	sf::Vector2i	pos		=	getPosition();
	sf::Vector2u	size	=	getSize();
	
#ifdef OS_WINDOWS
	if( Maximized() )
	{
		mMaximized		=	true;
		mMaximizedPos.Set( pos.x, pos.y );
	}
	else
#endif
	{
#ifdef OS_WINDOWS
		mMaximized	=	false;
#endif
		mPosition.Set( pos.x, pos.y );
		mSize.Set( size.x, size.y );
	}
}


#ifdef OS_WINDOWS
bool Application::GameWindow::Maximized() const
{
	sf::WindowHandle	handle			=	getSystemHandle();
	WINDOWPLACEMENT		winPlacement	=	{sizeof( WINDOWPLACEMENT ), 0};

	GetWindowPlacement( handle, &winPlacement );
	return winPlacement.showCmd==SW_MAXIMIZE;
}
#endif


void Application::GameWindow::_CreateWindow()
{
}
